package com.itcast.netty.handler.dispatcher;

import com.itcast.common.entity.MethodInvokeMeta;
import com.itcast.common.entity.NullWritable;
import com.itcast.common.entity.result.ResponseResult;
import com.itcast.common.enums.ResponseEnum;
import com.itcast.netty.utils.ResponseResultUtil;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.*;

/**
 * 请求分排器
 *
 * @author zheng.zhang
 */
@Slf4j
@Component
public class RequestDispatcher implements ApplicationContextAware {
    /**
     * 应用上下文
     */
    private ApplicationContext app;
    /**
     * 手动创建线程池
     */
    private final ExecutorService executorService = new ThreadPoolExecutor(2, 5,
            1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(3),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    /**
     * 发送
     *
     * @param ctx        通道上下文
     * @param invokeMeta 方法元信息
     */
    public void dispatcher(ChannelHandlerContext ctx, final MethodInvokeMeta invokeMeta) {
        executorService.submit(() -> {
            ChannelFuture future;
            try {
                Class<?> interfaceClass = invokeMeta.getInterfaceClass();
                String methodName = invokeMeta.getMethodName();
                Object[] args = invokeMeta.getArgs();
                Class<?>[] parameterTypes = invokeMeta.getParameterTypes();
                Object targetBean = app.getBean(interfaceClass);
                Method method = targetBean.getClass().getMethod(methodName, parameterTypes);
                Object returnObj = method.invoke(targetBean, args);
                if (returnObj == null) {
                    future = ctx.writeAndFlush(NullWritable.nullWritable());
                } else {
                    future = ctx.writeAndFlush(returnObj);
                }
            } catch (Exception e) {
                log.info("发送信息失败: {}", e.getMessage());
                ResponseResult<?> error = ResponseResultUtil.error(ResponseEnum.SERVER_ERROR);
                future = ctx.writeAndFlush(error);
            }
            future.addListener(ChannelFutureListener.CLOSE);
        });
    }

    /**
     * 加载当前application.xml
     *
     * @param applicationContext 应用上下文
     * @throws BeansException Bean异常
     */
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.app = applicationContext;
    }
}
